//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
///
//==================================================================

#include <string.h>
#include <gl/glew.h>
#include "psys.h"
#include "texture.h"

//==================================================================
using namespace PUtils;

//==================================================================
#ifdef _DEBUG
#define PGLERRORCHECK			PSYS::GLErrorCheck( _T(__FILE__), __LINE__ )
#else
#define PGLERRORCHECK			(0)
#endif

//==================================================================
#define INVAL_TEX	0

//==================================================================
static void upload_ogl_tex32( int w, int h, const u_char *datap, int src_pitch, bool source_is_24 )
{
	if ( source_is_24 )
	{
		glTexSubImage2D( GL_TEXTURE_2D, 0, 0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, datap );
	}
	else
	{
		glTexSubImage2D( GL_TEXTURE_2D, 0, 0,0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, datap );
	}
}

//==================================================================
static int log2( u_int val )
{
	int log2;

	for (log2=0; (1 << log2) < val; ++log2)
		;

	return log2;
}

//==================================================================
texture_t::texture_t( PUtils::ImageBase *imgbasep ) :
	_tex_ogl_id(0),
	_w2(0),
	_h2(0),
	_scoe(0),
	_tcoe(0),
	_flags(1),	// needs update
	_imgbasep(imgbasep)
{
	_w2 = 1 << log2( imgbasep->GetWidth() );
	_h2 = 1 << log2( imgbasep->GetHeight() );
	Update();
}

//==================================================================
texture_t::~texture_t()
{
	if ( _tex_ogl_id != 0 )
		glDeleteTextures( 1, &_tex_ogl_id );
}

//==================================================================
void texture_t::Update()
{
	// if was invalidated, then proceed to update
	if ( _flags & 1 )
		_flags &= ~1;

	bool	source_is_24 = false;
	int		w = _imgbasep->GetWidth();
	int		h = _imgbasep->GetHeight();


	if ( _tex_ogl_id == 0 )
	{
		glGenTextures( 1, &_tex_ogl_id );
		PGLERRORCHECK;

		glBindTexture( GL_TEXTURE_2D, _tex_ogl_id );
		PGLERRORCHECK;

		int	ww = w;
		int	hh = h;

		if ( w != 1 << log2( w ) )
			ww = 1 << (log2( w ));

		if ( h != 1 << log2( h ) )
			hh = 1 << (log2( h ));

		switch ( _imgbasep->GetBaseFormat() )
		{
		case ImageBase::BF_INDEX8:
			glTexImage2D( GL_TEXTURE_2D, 0, 1, ww, hh, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL );
			break;

		case ImageBase::BF_L8A8:
			glTexImage2D( GL_TEXTURE_2D, 0, 2, ww, hh, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL );
			break;

		case ImageBase::BF_L8:
			glTexImage2D( GL_TEXTURE_2D, 0, 1, ww, hh, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL );
			break;

		case ImageBase::BF_R8G8B8:
			glTexImage2D( GL_TEXTURE_2D, 0, 3, ww, hh, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL );
			break;

		case ImageBase::BF_B8G8R8:
			glTexImage2D( GL_TEXTURE_2D, 0, 3, ww, hh, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, NULL );
			break;

		case ImageBase::BF_R8G8B8A8:
			glTexImage2D( GL_TEXTURE_2D, 0, 4, ww, hh, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
			break;

		case ImageBase::BF_B8G8R8A8:
			glTexImage2D( GL_TEXTURE_2D, 0, 4, ww, hh, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL );
			break;

		default:
			PASSERT( 0 );	// not really supported for now
			break;
		}

		PGLERRORCHECK;
		_scoe = (float)1.0f / ww;
		_tcoe = (float)1.0f / hh;

		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
		//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
		//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
	}
	
	glBindTexture( GL_TEXTURE_2D, _tex_ogl_id );
	PGLERRORCHECK;

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
	PGLERRORCHECK;

	switch ( _imgbasep->GetBaseFormat() )
	{
	case ImageBase::BF_INDEX8:
		glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, w, h, GL_ALPHA, GL_UNSIGNED_BYTE, _imgbasep->GetData() );
		PGLERRORCHECK;
		break;

	case ImageBase::BF_L8A8:
		glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, w, h, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, _imgbasep->GetData() );
		PGLERRORCHECK;
		break;

	case ImageBase::BF_L8:
		glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, w, h, GL_LUMINANCE, GL_UNSIGNED_BYTE, _imgbasep->GetData() );
		PGLERRORCHECK;
		break;

	case ImageBase::BF_R8G8B8:
		upload_ogl_tex32( w, h, _imgbasep->GetData(), _imgbasep->GetImagePitch(), true );
		break;

	case ImageBase::BF_R8G8B8A8:
		upload_ogl_tex32( w, h, _imgbasep->GetData(), _imgbasep->GetImagePitch(), false );
		break;

	default:
		PASSERT( 0 );	// not really supported for now
		break;
	}
}

//==================================================================
void texture_t::Select()
{
	glBindTexture( GL_TEXTURE_2D, _tex_ogl_id );
	glMatrixMode( GL_TEXTURE );
		glLoadIdentity();
		glScalef( _imgbasep->GetWidth() * _scoe, _imgbasep->GetHeight() * _tcoe, 1 );
	glMatrixMode( GL_MODELVIEW );
}

//==================================================================
void texture_t::Invalidate()
{
	_flags |= 1;
}
